---
title: Why Immutability
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import whyImmutability from "./why_immutability"
import {
  trimSnippet,
  AutoSnippet,
  When,
} from "../../src/components/CodeSnippet";

## What is Immutability?

Immutability is when all fields of an `Object` are final or late final.
They are set exactly once upon construction.

Immutability is desireable for many different reasons

- Value equality rather than reference equality
- Local reasoning about a piece of code
  - A far distant piece of code can't obtain a reference and change the object from underneath you
- Easier to reason about for asynchronous and parallel tasks
  - Other code can't mutate your object in between operations
- Safety of APIs
  - What you pass into a method cannot be changed by the callee / caller

A copyWith method helps with reducing verbosity when creating a new object with just a few things changed.

Copying is more efficient than you might think, since dart can reuse any references to sub-objects that have not changed.
:::warning
Make sure your objects are deeply immutable, otherwise you'll have to implement some sort of deep copy mechanism.
:::

## Best Practices

You can use any package you want to create immutable state.

For immutable objects:

- [package:freezed](https://pub.dev/packages/freezed)
- [package:built_value](https://pub.dev/packages/built_value)

For immutable collections (Map, Set, List):

- [package:fast_immutable_collections](https://pub.dev/packages/fast_immutable_collections)
- [package:built_collection](https://pub.dev/packages/built_collection)
- [package:kt_dart](https://pub.dev/packages/kt_dart)
- [package:dartz](https://pub.dev/packages/dartz)

It is highly recommended to use [freezed],
since it has several nice additions beyond just making immutable objects including:

- A generated copyWith method
- Deep copy (copyWith on nested freezed objects)
- Union types
- Union mapping functions

You do not need to use code generation to work with immutable state, but it makes it much easier.

:::warning
If you want to use the built-in collections, make sure to enforce a discipline of making copies of collections when updating them.
The issue with not copying a collection is that riverpod determines whether to emit a new state based on whether the reference to the object has changed.
If you just call a method that mutates an object, the reference is the same.
:::

### Using immutable state

Immutable state is best fit for using a [Notifier] <When codegen={false}> in combination with [NotifierProvider] </When>.
A [Notifier] allows you to expose an interface through which you can 'mutate' the state.
You cannot mutate the state from outside the class you define that extends [Notifier].
This enforces a separation of concerns and keeps business logic outside of your UI.

Here is an example of a simple immutable settings class for changing an app theme.

<AutoSnippet language="dart" {...whyImmutability}></AutoSnippet>

To use this code, remember to import `freezed_annotation` add the part directive and run [build_runner] to generate the freezed classes!

[changenotifier]: https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html
[statenotifier]: https://pub.dev/documentation/riverpod/latest/riverpod/StateNotifier-class.html
[statenotifierprovider]: https://pub.dev/documentation/riverpod/latest/riverpod/StateNotifierProvider-class.html
[notifier]: https://pub.dev/documentation/riverpod/latest/riverpod/Notifier-class.html
[notifierprovider]: https://pub.dev/documentation/riverpod/latest/riverpod/NotifierProvider.html
[asyncvalue]: https://pub.dev/documentation/riverpod/latest/riverpod/AsyncValue-class.html
[freezed]: https://pub.dev/packages/freezed
[build_runner]: https://pub.dev/packages/build_runner
